<template>
  <my-pannel headerTitle="Visual Map">
    <el-col class="map-container">
      <l-map
        ref="mapObject"
        :zoom="zoom"
        :center="center"
        :leaflet-options="{ preferCanvas: true }"
        @ready="onMapReady"
      >
        <!-- 底图图层 -->
        <l-tile-layer
          :url="baseMapUrls[selectedBaseMapIndex].baseMapUrl"
          layer-type="base"
        ></l-tile-layer>
        <!-- 底图切换控件 -->
        <l-control position="topright">
          <el-dropdown placement="left-start" size="small" :hide-on-click="false" trigger="click">
            <el-button circle>
              <img class="icon-img" src="@/assets/img/map-controller-icon/baseLayers-icon.png" />
            </el-button>
            <template #dropdown>
              <el-dropdown-menu>
                <el-radio-group
                  v-model="selectedBaseMapIndex"
                  size="small"
                  class="basemap-layers-control"
                >
                  <el-dropdown-item v-for="(baseMap, index) in baseMapUrls" :key="index">
                    <el-radio :label="index">{{ baseMap.baseMapName }}</el-radio>
                  </el-dropdown-item>
                </el-radio-group>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </l-control>
        <!-- 图层树控件 -->
        <l-control position="topleft">
          <el-dropdown placement="right-start" size="small" :hide-on-click="false" trigger="click">
            <el-button circle>
              <img class="icon-img" src="@/assets/img/map-controller-icon/layerTree-icon.png" />
            </el-button>
            <template #dropdown>
              <el-dropdown-menu>
                <el-tree
                  :data="traLayersTree"
                  :props="treeProps"
                  node-key="label"
                  show-checkbox
                  draggable
                  :allow-drag="allowDrag"
                  :allow-drop="allowDrop"
                  default-expand-all
                  :default-checked-keys="checkedKeys"
                  :expand-on-click-node="false"
                  @check-change="handleCheckChange"
                  @check="handleCheck"
                  @node-click="nodeClick"
                  @node-drop="nodeDrop"
                >
                  <template #empty>
                    <el-row class="empty-popup">
                      <el-col :span="24"><span>No Data!</span></el-col>
                    </el-row>
                  </template>
                  <template #default="{ node }">
                    <span>{{ node.label }}</span>
                    <span style="flex-grow: 1; min-width: 20px"></span>
                    <el-popconfirm
                      title="Are you sure to delete this?"
                      width="300px"
                      @confirm="deleteTraLayers(node, $event)"
                      v-if="node.childNodes.length > 0 && !/-?\d+(\.\d+)?/.test(node.label)"
                    >
                      <template #reference>
                        <el-icon
                          @mouseover="
                            (e) => {
                              e.target.setAttribute('opacity', 0.5);
                            }
                          "
                          @mouseout="
                            (e) => {
                              e.target.setAttribute('opacity', 1);
                            }
                          "
                          ><CloseBold
                        /></el-icon>
                      </template>
                    </el-popconfirm>
                  </template>
                </el-tree>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </l-control>
      </l-map>
    </el-col>
  </my-pannel>
</template>

<script>
import MyPannel from '@/components/MyPannel.vue';
import 'leaflet/dist/leaflet.css';
import L, { featureGroup } from 'leaflet';
import {
  LMap,
  LTileLayer,
  LControl,
  LControlLayers,
  LFeatureGroup,
  LPolyline,
  LPopup,
  LCircle,
} from '@vue-leaflet/vue-leaflet';
import { mapState } from 'vuex';
import { TraLayersTreeNode } from '@/util/type/TraLayersTreeNode.js';
export default {
  data() {
    return {
      map: null,
      lineFeatureGroups: [], //记录地图中的线图层
      fractionFeatureGroup: L.featureGroup(), //专门记录地图中线要素的折点图层,在点击线要素时绘制
      zoom: 12, //地图缩放等级
      center: [26.074508, 119.296482], //地图中心位置
      //部分可选的底图
      baseMapUrls: [
        {
          baseMapName: 'VecMap',
          baseMapUrl: `https://tile.openstreetmap.de/{z}/{x}/{y}.png`,
        },
        {
          baseMapName: 'BlackVecMap',
          baseMapUrl: `https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png`,
        },
        {
          baseMapName: 'ImgMap',
          baseMapUrl: `https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}`,
        },
      ],
      //图层树属性映射
      treeProps: {
        children: 'children',
        label: 'label',
      },
      selectedBaseMapIndex: 0, //选择的底图索引
      traLayersTree: [], //图层树对象
      checkedKeys: [], //当前被选中的图层的key
    };
  },
  computed: {
    ...mapState(['traObjects']), //从vuex获取轨迹对象集合
  },
  watch: {
    traObjects: {
      //监听traObjects的变化
      handler(newValue, oldValue) {
        // this.traLayersTree = TraLayersTreeNode.createTree(newValue); //重新构建图层树对象
        //添加新的对象
        if (newValue.length > this.traLayersTree.length) {
          this.traLayersTree.push(TraLayersTreeNode.transToNode(newValue[newValue.length - 1]));
        }
        //删除对象
        else if (newValue.length < this.traLayersTree.length) {
          let newTraObjectNames = newValue.map((traobject) => traobject.fileName); //映射为标志数组
          //找到需要删除的对象
          let deleteTraNode = this.traLayersTree.filter(
            (traNode) => newTraObjectNames.indexOf(traNode.label) == -1,
          )[0];
          this.traLayersTree.splice(this.traLayersTree.indexOf(deleteTraNode), 1); //删除元素
        }
      },
      deep: true,
    },
    traLayersTree: {
      //监听traLayersTree的变化
      handler(newValue, oldValue) {
        //根据traLayersTree的变化创建若干featuregroup用于存储每一个线要素图层
        for (let treeNode of newValue) {
          // 判断当前地图是否存在有相同label的图层组
          let exitsFeatureGroup = this.lineFeatureGroups.find((featureGroup) => {
            return featureGroup.label === treeNode['label'];
          });
          if (exitsFeatureGroup) continue;
          else {
            let featureGroup = L.featureGroup().addTo(this.map); //创建一个featuregroup并加入地图中
            featureGroup.label = treeNode['label']; //对featuregroup进行标识
            this.lineFeatureGroups.push(featureGroup); //记录到数组中
          }
        }
      },
      deep: true,
    },
  },
  mounted() {
    // this.traLayersTree = TraLayersTreeNode.createTree(this.traObjects); //更新图层树对象
    // for (let traobject in this.traObjects) {
    //   this.traLayersTree.push(TraLayersTreeNode.transToNode(traobject));
    // }
  },
  methods: {
    // 在地图准备就绪后的钩子函数
    onMapReady(map) {
      this.map = map;
      this.fractionFeatureGroup.addTo(this.map); //初始化一个折点图层组
    },
    //图层选中变更事件
    handleCheckChange(selectedNode, isChecked, isChildChecked) {
      //如果访问子节点
      if (!selectedNode['children'].length > 0) {
        //找到当前子节点对应的featuregroup
        let featureGroup = this.lineFeatureGroups.find((featureGroup) => {
          return (
            featureGroup.label ===
            selectedNode['label'].substring(0, selectedNode['label'].lastIndexOf('_'))
          );
        });
        //如果子节点被选中
        if (isChecked) {
          let polyLine = L.polyline(
            selectedNode['coords'].map((coord) => [coord[1], coord[0]]),
            { color: selectedNode['color'] },
          ).addTo(featureGroup); //创建线要素
          polyLine.label = selectedNode['label']; //标识
          this.layerSort(); //排序
          // 线要素点击事件
          polyLine.on('click', (event) => {
            L.popup()
              .setContent(selectedNode['label'])
              .setLatLng(polyLine.getCenter())
              .openOn(this.map); //弹窗
            this.fractionFeatureGroup.clearLayers(); //清空折点图层组
            // 遍历坐标数组，在每个坐标点上绘制一个标记
            polyLine.getLatLngs().forEach((latlng) => {
              let fraction = L.circle(latlng, { color: 'rgb(0,0,0)', radius: 50 }).addTo(
                this.fractionFeatureGroup,
              );
              fraction.bindPopup('location：[' + latlng.lng + ',' + latlng.lat + ']'); //坐标
              fraction.label = selectedNode['label']; //标识
            });
          });
          this.map.fitBounds(polyLine.getBounds()); //调整地图视角
        }
        //如果子节点被取消选中
        else if (!isChecked) {
          for (let lineLayer of featureGroup.getLayers()) {
            if (lineLayer.label == selectedNode['label']) {
              featureGroup.removeLayer(lineLayer); //清除线图层
              break;
            }
          }
          if (this.fractionFeatureGroup.getLayers().length > 0) {
            let fractionLayer = this.fractionFeatureGroup.getLayers()[0];
            if (fractionLayer.label === selectedNode['label'])
              this.fractionFeatureGroup.clearLayers(); //清除对应的折点
          }
        }
      }
    },
    //图层显示排序
    layerSort() {
      //遍历图层树结构
      for (let treeNode of this.traLayersTree) {
        let label = treeNode.label; //获得当前根节点的标签
        //在featuregroup中找到其下的所有线图层
        for (let featureGroup of this.lineFeatureGroups) {
          if (featureGroup.label === label) {
            featureGroup.bringToBack(); //图层置于最底层
          }
        }
      }
    },
    //节点点击事件
    nodeClick(node) {
      if (node.children.length > 0) return;
      //找到当前子节点对应的featuregroup
      let featureGroup = this.lineFeatureGroups.find((featureGroup) => {
        return featureGroup.label === node.label.substring(0, node.label.lastIndexOf('_'));
      });
      //触发线要素的点击事件
      for (let lineLayer of featureGroup.getLayers()) {
        if (lineLayer.label === node.label) {
          lineLayer.getElement().dispatchEvent(
            new MouseEvent('click', {
              bubbles: true,
              cancelable: true,
              view: window,
            }),
          );
        }
      }
    },
    // 图层选中事件
    handleCheck(param1, param2) {
      this.checkedKeys = param2.checkedKeys; //获取当前选中的图层的key
    },
    //删除图层组
    deleteTraLayers(node, event) {
      this.$store.commit('deleteTraObjects', node.data.label); //将删除提交到vuex
      //找到对应的featuregroup
      let featureGroup = this.lineFeatureGroups.find((featureGroup) => {
        return featureGroup.label === node.data.label;
      });
      this.map.removeLayer(featureGroup); //删除图层组
      //从图层组记录中删除
      this.lineFeatureGroups = this.lineFeatureGroups.filter((featureGroup) => {
        return featureGroup.label != node.data.label;
      });

      if (this.fractionFeatureGroup.getLayers().length > 0) {
        let fractionLayer = this.fractionFeatureGroup.getLayers()[0];
        if (
          fractionLayer.label.substring(0, fractionLayer.label.lastIndexOf('_')) === node.data.label
        )
          this.fractionFeatureGroup.clearLayers(); //清除对应的折点
      }
      //删除选中的节点信息
      this.checkedKeys = this.checkedKeys.filter(
        (key) => !new RegExp(`^${node.data.label}(_\\d+)?$`).test(key),
      );
    },
    //设置允许拖拽的元素
    allowDrag(node) {
      // 只允许拖拽第一级节点
      return node.level === 1;
    },
    //设置允许拖拽的位置
    allowDrop(draggingNode, dropNode, type) {
      return dropNode.level === 1 && type != 'inner';
    },
    //拖拽完成的任务
    nodeDrop() {
      this.layerSort(); //排序图层
    },
  },
  components: {
    MyPannel,
    LMap,
    LTileLayer,
    LControl,
    LControlLayers,
    LFeatureGroup,
    LPolyline,
    LPopup,
    LCircle,
  },
};
</script>

<style lang="scss" scoped>
//地图容器样式
.map-container {
  height: $map-height;
}
//地图控件样式
.leaflet-control {
  .el-button {
    opacity: 0.7;
  }
}

//地图控件弹出框样式
.el-dropdown-menu {
  // 底图控件弹出框样式
  .basemap-layers-control {
    align-items: flex-start;
    flex-direction: column;
  }

  // 图层控件弹出框样式
  .el-tree {
    font-size: inherit;
    margin: auto 10px auto auto;
    max-height: 250px;
    overflow: auto;
    // 当图层为空时的样式
    :deep(.el-tree__empty-block) {
      min-height: 0;

      .empty-popup {
        align-items: center;
        margin: 2px 0 2px 2px;
      }
    }
    // 图层树中所有x的样式
    .el-link {
      font-size: inherit;
    }
  }
}
</style>
